/*
 Copyright (c) 2013 MeherTJ G. All rights reserved.
 License: LGPL for personnal study or free software.
 */

#pragma once

#include "zen_image.h"
#include "zen_log.h"
#include "zen_endian.h"
#include "zen_exception.h"

namespace Zen
{

    /**
	 This(raw) is a simple bitmap image format.
	 definition: header(8 bytes) + value(pixel bytes in order).
	 header = sign(3B, 'jai') + format(1B) + width(2B) + height(2B). big endian order.
	 */

    struct ImageRDecoder : ImageDecoder
    {
        virtual std::shared_ptr<Image> decode(std::vector<uint8_t> const& value);
    };

    struct ImageREncoder : ImageEncoder
    {
        virtual std::vector<uint8_t> encode(Image const&) override;
    };
} // namespace Zen

////////////////////////

namespace Zen
{
    struct ImageRHead
    {
        char sign[4] = "img";
        uint16_t width = 0;
        uint16_t height = 0;

        void Set(uint16_t width_, uint16_t height_, EPixel fmt)
        {
            width = HostNet16(width);
            height = HostNet16(height);
            sign[3] = (int)fmt;
        }
        EPixel PixelType()
        {
            return (EPixel)sign[3];
        }
    };

    inline std::shared_ptr<Image> ImageRDecoder::decode(std::vector<uint8_t> const& value)
    {
        ImageRHead head;
        __zen_must_else(value.size() >= sizeof(head), "invalid image raw");

        ::memcpy(&head, value.data(), sizeof(head));

        uint32_t width = HostNet32(head.width);
        uint32_t height = HostNet32(head.height);

        uint32_t bpp = (int)head.PixelType();
        ;

        __zen_must_else(bpp > 0 && bpp <= 4, "invalid format");

        uint32_t sz = width * height * bpp;

        __zen_must_else(value.size() >= sz + sizeof(head), "too few value length");

        auto image = Image::GenerateBlank(head.PixelType(), width, height);
        __zen_must(image->Size() == sz);

        ::memcpy(image->Data(), value.data() + sizeof(head), sz);
        return image;
    }

    inline std::vector<uint8_t> ImageREncoder::encode(Image const& image)
    {
        ImageRHead head;

        head.Set((uint32_t)image.Width(), (uint32_t)image.Height(), image.PixelType());

        auto head_buf = reinterpret_cast<char const*>(&head);

        std::vector<uint8_t> value;
        value.reserve(sizeof(head) + image.Size());
        value.assign(head_buf, head_buf + sizeof(head));
        value.insert(value.end(), image.Data(), image.Data() + image.Size());

        return value;
    }
} // namespace Zen
